/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lvyh.lightframe.core.provider.transport;

import com.lvyh.lightframe.common.RpcContext;
import com.lvyh.lightframe.core.config.ServerConfig;
import com.lvyh.lightframe.core.eventbus.EventBusCenter;
import com.lvyh.lightframe.core.eventbus.ServerStartedEvent;
import com.lvyh.lightframe.core.eventbus.ServerStoppedEvent;
import com.lvyh.lightframe.common.ext.Spi;
import com.lvyh.lightframe.core.codec.NettyDecoder;
import com.lvyh.lightframe.core.codec.NettyEncoder;
import com.lvyh.lightframe.core.invoke.request.Heartbeat;
import com.lvyh.lightframe.core.invoke.request.RpcRequest;
import com.lvyh.lightframe.core.invoke.response.RpcResponse;
import com.lvyh.lightframe.core.util.SpringContextUtils;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

@Spi("nettyserver")
public class NettyServerBootstrap extends ProviderBootstrap {

    private Thread thread;

    @Override
    public void startup() throws Exception {

        thread = new Thread(new Runnable() {
            @Override
            public void run() {

                EventLoopGroup bossGroup = new NioEventLoopGroup();
                EventLoopGroup workerGroup = new NioEventLoopGroup();

                /**
                 * IdleStateHandler heartbeat mechanism is mainly used to detect whether the remote is alive or not.
                 * If it is not alive or active, idle socket connection will be processed to avoid the waste of resources
                 */
                try {
                    ServerBootstrap bootstrap = new ServerBootstrap();
                    bootstrap.group(bossGroup, workerGroup);
                    bootstrap.channel(NioServerSocketChannel.class);
                    bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            pipeline.addLast(new IdleStateHandler(0, 0, Heartbeat.INTERVAL * 3, TimeUnit.SECONDS));
                            pipeline.addLast(new NettyDecoder(RpcRequest.class));
                            pipeline.addLast(new NettyEncoder(RpcResponse.class));
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });
                    bootstrap.childOption(ChannelOption.TCP_NODELAY, true);
                    bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);

                    ChannelFuture future = bootstrap.bind(RpcContext.getPort()).sync();
                    logger.info("[NettyServerBootstrap] server startup success, ip: {}, port: {}", RpcContext.getLocalIp(), RpcContext.getPort());

                    EventBusCenter eventBusCenter = SpringContextUtils.getBean(EventBusCenter.class);
                    ServerConfig serverConfig = SpringContextUtils.getBean(ServerConfig.class);

                    logger.info("[NettyServerBootstrap] server startup success and message delivery to event bus.");
                    eventBusCenter.postSync(new ServerStartedEvent(serverConfig));

                    future.channel().closeFuture().sync();

                } catch (Exception e) {
                    logger.error("[NettyServerBootstrap] server startup error.", e);
                } finally {
                    workerGroup.shutdownGracefully();
                    bossGroup.shutdownGracefully();
                }
            }
        });
        thread.setDaemon(true);
        thread.start();

    }

    @Override
    public void stop() throws Exception {

        if (thread != null && thread.isAlive()) {
            thread.interrupt();
        }

        EventBusCenter eventBusCenter = SpringContextUtils.getBean(EventBusCenter.class);
        ServerConfig serverConfig = SpringContextUtils.getBean(ServerConfig.class);
        eventBusCenter.postSync(new ServerStoppedEvent(serverConfig));
        logger.info("[netty server] rpc server stop event msg post success!");
    }

}
